package pcb.models 
{
	import com.intel.patterns.observer.Subject;
	import com.intel.utils.Debugger;
	import flash.display.MovieClip;
	import flash.utils.Dictionary;
	import pcb.models.events.ModelEvent;
	import pcb.models.vo.ILessonPart;
	import pcb.models.vo.Interaction;
	import pcb.models.vo.IPage;
	import pcb.models.vo.LessonPart;
	import pcb.models.vo.PageTypes;
	import pcb.models.vo.Slide;
	import flash.system.System;

	
	/**
	 * The LessonStateModel will store the main data structure of the lesson.
	 * A lesson will consist of one or more LessonParts and a LessonPart will
	 * be comprised of one or more Slides.  Slides present the multimedia
	 * content of the lesson.
	 * 
	 * A basic state model (status) is also in place here that defines the current status
	 * of the lesson and provides methods to change the lesson state.
	 * 
	 * Views can be notified of changes to the status via notifications of 
	 * type Notifications.LESSON_STATUS_CHANGED
	 * 
	 * @author Fintan Boyle
	 */
	public class LessonStateModel extends Subject implements IModel 
	{
		public static const PREINITIALISED:String = "preinitialised";
		public static const INITIALISED:String = "initialised";
		public static const STOPPED:String = "stopped";
		public static const PAUSED:String = "paused";
		public static const PLAYING:String = "playing";
		public static const NEXT_SLIDE:String = "next_slide";
		public static const NEXT_INTERACTIVITY:String = "next_interactivity";		
		public static const PREVIOUS_SLIDE:String = "previous_slide";
		public static const LOADING_SLIDE:String = "loading_slide";
		public static const END_OF_SLIDE:String = "end_of_slide";
		public static const LESSON_FINISHED:String = "lesson_finished";
		
		/**
		 * simple state model that excludes the background asset loading sequence
		 */
		private var _status:String = PREINITIALISED;
		private var _lessonId:String;
		
		/**
		 * mainStage will reference the loaded lesson swf
		 */
		public var mainStage:MovieClip;
		
		public var onFirstSlide:Boolean = true; //true for PREINITIALISED state
		private var _onLastSlide:Boolean = true; //true for PREINITIALISED state
		
		/**
		 * isLoaded() reflects whether or not all Assets (IAssets) that belong to
		 * the lesson have been loaded.
		 * 
		 * If all assets have not been loaded, a command triggered from a user
		 * gesture will reshuffle the order of the loadQueue so that the selected
		 * slide's assets will move to the top of the queue
		 * 
		 */
		public var _loaded:Boolean = false;
		
		/**
		 * The display title of the current lesson
		 */
		private var lessonTitle:String = "";
		
		/**
		 * A list of lessonParts (ILessonPart) that the lesson contains.
		 * It is likely that lessons will usually only contain one part.
		 */
		private var lessonParts:Vector.<ILessonPart>;
		
		/**
		 * convenience property for use in nextPage() and prevPage()
		 * currentPageId will typically correspond to a slide id
		 */
		private var _currentPageIndex:Number = -1;
		
		/**
		 * a slide count that omits pages that have END_OF_LESSON types
		 */
		private var _totalSlides:int = 0;
		
		/**
		 * references currently selected page value object (IPage) for the views 
		 * to access when notified
		 */
		public var currentPage:IPage;
		
		/**
		 * LessonParts are each located on an exclusive frame on the maintimeline.
		 * This index will correspond to the order of lessonParts defined in
		 * structure.xml.  lessonPart.getId() will be used to navigate to a 
		 * lessonPart frame.  Therefore, the ordering of frames on the maintimeline
		 * should not matter.
		 */
		//private var _currentLessonPartIndex:Number = -1;
		
		//sound mute state
		private var _isMuted:Boolean = false;
		
		//lists all pages
		//for use in local next and prev functionality
		private var pages:Vector.<IPage>;
		//for use in local next and prev functionality
		private var _pageIds:Array;
		
		public function LessonStateModel() 
		{
			Debugger.traceOut("LessonStateModel constructed");
			
			lessonParts = new Vector.<ILessonPart>;
			pages = new Vector.<IPage>;
			_pageIds = new Array();
		}
		
		public function reset():void 
		{
			_status = PREINITIALISED;
			onFirstSlide = true; //true for PREINITIALISED state
			_onLastSlide = true; //true for PREINITIALISED state
			lessonTitle = "";
			isMuted = false;
			
			lessonParts = new Vector.<ILessonPart>;
			pages = new Vector.<IPage>;
			_pageIds = new Array();
			
			_currentPageIndex = -1;
			_totalSlides = 0;
			
			//mainStage.loaderInfo.loader.unloadAndStop();
			mainStage = null;
		}
		
		/**
		 * moves to next slide
		 * It doesn't distinguish between Slide and LessonPart types
		 */
		public function nextPage():void 
		{
			if (_currentPageIndex < pages.length-1) {
				
				_currentPageIndex++
				updateFirstLastSlide();
				
				status = NEXT_SLIDE ;
				
				playPage(pages[_currentPageIndex]);				
				
			}
			
			chkLessonEnded();
		}
		
		public function chkLessonEnded():Boolean
		{
			if(_currentPageIndex >= pages.length - 1) {
				//should never be greater than, only equal to
				status = LESSON_FINISHED;				
				return true;
			}
			
			return false;
		}
		
		public function endOfSlideChk():void 
		{
			//we don't want to notify observers if the lesson has finished
			if(_currentPageIndex < pages.length - 1){
				status = END_OF_SLIDE;
			}
		}
		
		public function nextInteractivity():void 		
		{
			Debugger.traceOut("lesson state model next");
			if (_interactionIndex >= _interactionIDs.length - 1) {				
				nextPage();
			} else {				
				_interactionIndex++;
				status = NEXT_INTERACTIVITY;				
			}			
		}
		
		
		/**
		 * moves to previous slide
		 * It doesn't distinguish between Slide and LessonPart types
		 */
		public function prevPage():void 
		{
			if(_currentPageIndex > 0){
				_currentPageIndex--;
				
				updateFirstLastSlide();
				
				status = PREVIOUS_SLIDE;
				
				playPage(pages[_currentPageIndex]);
			}
		}
		
		public function quit():void 
		{
			//daveh
			System.exit(0);
		}
		
		public function startPagePlay():void 
		{
			//TODO shouldn't need to check status if view reflects current state?
			
			if (status == STOPPED || status == PAUSED || status == INITIALISED) {
				
				if (_currentPageIndex >= pages.length - 1) {
					_currentPageIndex = -1;
				}
				
				if (_currentPageIndex > -1) {
					playPage(pages[_currentPageIndex]);
				}else {
					nextPage();
				}
				
			}else if (status == LESSON_FINISHED) {
				
				_currentPageIndex = -1;
				nextPage();
				
			}else {
				Debugger.traceOut("state not handled in startPagePlay(): "+status, Debugger.WARNING);
			}
			
		}
		
		public function stopPagePlay():void 
		{
			status = STOPPED;			
		}
		
		public function pausePlay():void 
		{
			status = PAUSED;
		}
		
		private function playPage(page:IPage):void 
		{
			loadPage(page);
		}
		
		private function loadPage(page:IPage):void 
		{
			
			if ( page.getPageType() == PageTypes.SLIDE) {
				
				currentPage = page;
				
				resetInteractionState();
				
				if (Slide(currentPage).getPedagogy() == Slide.INTERACTIVE) {
					initInteractionState();
				}
				
				if (Slide(currentPage).hasVO()) { //only applicable to PageTypes.END_OF_LESSON so far but left this in for now
				
					if (Slide(currentPage).getVO().isLoaded()) {
						
						Debugger.traceOut("Slide(page).getVO().isLoaded " + Slide(page).getVO().isLoaded());
						play();
						
					}else {
						
						Slide(currentPage).addEventListener(ModelEvent.LOADED, currentPage_onLoaded);
						Debugger.traceOut("Slide(page).getVO().isLoaded -- " + Slide(page).getVO().isLoaded());
						status = LOADING_SLIDE;
						
						
					}
				}else {
					play();
				}
				
			}else if (page.getPageType() == PageTypes.END_OF_LESSON) {
				
				currentPage = page;
				
				resetInteractionState();
				
				Debugger.traceOut("it's an END_OF_LESSON slide");
				play();
				
			}else {
				Debugger.traceOut("it's not a slide!  it must be a lessonPart cover page (front or back)");
			
			}
			
		}
		
		private var _interactionIDs:Array;
		private var _interactionIndex:Number;
		
		private function resetInteractionState():void 
		{
			_interactionIDs = null;
			_interactionIndex = -1;
		}
		
		private function initInteractionState():void 
		{
			_interactionIDs = Slide(currentPage).getInteractionIDs();
			_interactionIndex = 0;
		}
		
		public function getCurrentInteractionId():String
		{
			return _interactionIDs[_interactionIndex];			
		}
		
		private function play():void 
		{
			status = PLAYING;
		}
		
		private function currentPage_onLoaded(e:ModelEvent):void 
		{
			Debugger.traceOut((e.target as Slide).getId() + "loaded now...");
			if (e.id == (e.target as Slide).getId()) {
				Debugger.traceOut("correct id");
			}
			play();
			e.target.removeEventListener(ModelEvent.LOADED, currentPage_onLoaded);
		}
		
		private function updateFirstLastSlide():void 
		{
			//will be -1 at PREINITIALISED and INITIALISED states and >=0 there after
			onFirstSlide = (_currentPageIndex <= 0)?true:false;
			
			onLastSlide = (_currentPageIndex == pages.length - 1)?true:false;
			
		}
		
		/*public function nextLessonPart():void 
		{
			
		}
		public function prevLessonPart():void 
		{
			
		}*/
		
		public function addLessonPart(lessonPart:ILessonPart):void 
		{
			lessonParts.push(lessonPart);
			trace(">>>>  lessonPart added: " + lessonPart);
			//adds lessonPart to pages.<IPage> list
			//if (lessonPart.hasStartPage()) pages.push(lessonPart.getStartPage());
			//if (lessonPart.hasEndPage()) pages.push(lessonPart.getEndPage());
			
		}
		
		public function getLessonPart(index:Number):LessonPart
		{			
			return lessonParts[index] as LessonPart;
		}
		
		public function getLessonPartbyId(id:String):LessonPart
		{
			for each (var part:LessonPart in lessonParts) {
				
				if (part.getId() == id) {
					return part;
				}
			}
			return null;
		}
		
		/* getters and setters */
		
		/**
		 * lessonId will reference the id of a specific lesson to be viewed
		 * this id will typically correspond to the name of folder containing
		 * the lesson in the Applications root directory.
		 */
		public function get lessonId():String { return _lessonId; }
		
		public function set lessonId(value:String):void 
		{
			_lessonId = value;
			
			notify(Notifications.LESSON_ID_CHANGED);
		}
		
		/**
		 * status will be used to notify view so that navView will 
		 * visually reflect the model's lesson status
		 */
		public function get status():String { return _status; }
		
		public function set status(value:String):void 
		{
			_status = value;
			
			updateFirstLastSlide();
			notify(Notifications.LESSON_STATUS_CHANGED);
		}
		
		public function get currentPageIndex():Number { return _currentPageIndex+1; }
		
		/**
		 * same as currentPageIndex but takes END_OF_LESSON into account
		 */
		public function get currentSlideIndex():Number { 
			
			if (isEndOfLesson()) {
				return this.currentPageIndex - 1;
			}
			
			return this.currentPageIndex; 
		}
		
		private function isEndOfLesson():Boolean
		{
			if (this.currentPageIndex-1 == pages.length-1 && this.getTotalSlides() < pages.length) {
				return true;
			}
			return false;
		}
		
		public function set currentPageIndex(index:Number):void 
		{
			stopPagePlay();
			_currentPageIndex = index;
		}
		
		public function set onLastSlide(value:Boolean):void 
		{
			_onLastSlide = value;
			
			//this.status = STOPPED;
		}
		
		public function get onLastSlide():Boolean { return _onLastSlide; }
		
		//Observer uses isMuted at LessonStateModel.PLAYING
		public function get isMuted():Boolean { return _isMuted; }
		
		//Cmd_respondToUserGesture updates this value
		public function set isMuted(value:Boolean):void 
		{
			_isMuted = value;
		}
		
		public function isLoaded():Boolean
		{
			return _loaded;
		}
		
		public function setLoaded(boo:Boolean):void
		{
			_loaded = boo;
		}
		
		public function addPage(id:String, page:IPage, type:String=""):void {
			
			pages.push(page);
			_pageIds.push(id);
			
			if (type != PageTypes.END_OF_LESSON) {
				_totalSlides++;
			}
		}
		
		public function getCurrentPageId():String
		{
			return pages[_currentPageIndex].getId();
		}
		
		public function setCurrentLessonTitle(title:String):void
		{
			lessonTitle = title;
		}
		public function getCurrentLessonTitle():String
		{
			return lessonTitle;
		}
		
		
		public function getTotalSlides():Number
		{
			//should omit slides of type END_OF_LESSON, therefor don't return pages.length
			//return pages.length;
			return _totalSlides;
		}
	}
	
}